/*
 *  matrix.cpp
 *  vector
 *
 *  Created by Fredrik Gustafsson on 9/15/10.
 *  Copyright 2010 Kungliga Tekniska Högskolan. All rights reserved.
 *
 */

#include "matrix.h"

Matrix::Matrix( ) {}

Matrix::Matrix( std::size_t rows, std::size_t cols) : m_vectors(Vector< matrix_row >(rows)) {
	for (size_t i = 0; i<rows; ++i) {
		m_vectors[i] = matrix_row(cols);
	}
}

Matrix::Matrix( const Matrix& m) : m_vectors(m.m_vectors) {}

Matrix::Matrix(int size) : m_vectors(Vector< matrix_row >(size))  {
	for (size_t i = 0; i<size; ++i) {
		m_vectors[i] = matrix_row(size);
	}
}

Matrix::~Matrix( ) {
	//Should be OK?
}

Matrix::matrix_row& Matrix::operator[]( index i ) {
	return m_vectors[i];
}

const Matrix::matrix_row& Matrix::operator[]( index i ) const {
	return m_vectors[i];
}

std::size_t Matrix::rows() const {
	return m_vectors.size();
}

std::size_t Matrix::cols() const {
	if (rows()>0) {
		return m_vectors[0].size();
	}
	return 0;
}

Matrix Matrix::transpose( ) {
	Matrix transposed;
	for (size_t j = 0; j<cols(); ++j) {
		Vector<int> temp;
		for (size_t i = 0; i<rows(); ++i) {
			temp.push_back((*this)[i][j]);
		}
		transposed.m_vectors.push_back(matrix_row(temp));
	}
	return transposed;
}

Matrix Matrix::identity( ) {
	if (rows() != cols()) throw std::out_of_range("Matrix has to be square");
	Matrix m(rows());
	for (size_t i = 0; i<rows(); ++i) {
		m[i][i] = 1;
	}
	return m;
}

Matrix Matrix::operator-( ) const {
	return (*this)*(-1);
}

std::ostream & operator<<(std::ostream & stream, Matrix & m)
{
	stream << "[";
	for (size_t i = 0; i<m.rows(); ++i) {
		if (i != 0) {
			stream << ";";
		}
		
		for (size_t j = 0; j<m.cols(); ++j) {
			stream << " " << m[i][j];
		}
		
		if (i < m.rows()-1) {
			stream << " " << std::endl;
		}
	}
	stream << " ]";
	return stream;
}

std::istream& operator>> ( std::istream& stream, Matrix& m)
{
	int curr_read;
	bool end_matrix=false,end_row=false;
	m.m_vectors.clear();
	Vector<int> new_row;
	//Skip '[ '
	stream.ignore(2);
	while(1) {
		//READ STRUCTURE
		while(1)
		{
			int next = stream.peek();
			if(next == '-' || (next>='0' && next <='9')) {
				stream >> curr_read;
				break;
			} else if (next == ';') {
				stream.ignore(1);
				end_row = true;
				break;
			} else if (next == ']') {
				end_matrix = true;
				break;
			} else {
				stream.ignore(1);
			}
		}

		if (end_matrix) {
			m.m_vectors.push_back(Matrix::matrix_row(new_row));
			break;
		} else if (end_row) {
			m.m_vectors.push_back(Matrix::matrix_row(new_row));
			new_row.clear();
			end_row = false;
		} else {
			new_row.push_back(curr_read);
		}

	}
	return stream;
}

bool Matrix::operator== ( const Matrix& m) {
	if (rows()!=m.rows() || cols()!=m.cols()) {
		return false;
	}
	for (size_t i=0; i<rows(); ++i) {
		for (size_t j=0; j<cols(); ++j) {
			if ((*this)[i][j] != m[i][j]) {
				return false;
			}
		}
	}
	return true;
}

Matrix& Matrix::operator= ( const Matrix& m) {
	m_vectors = m.m_vectors;
	return *this;
}

Matrix Matrix::operator* ( int mult ) const {
	Matrix m(*this);
	for (size_t i = 0; i<rows(); ++i) {
		for (size_t j = 0; j<cols(); ++j) {
			m[i][j]*=mult;
		}
	}
	return m;
}

Matrix Matrix::operator+ ( const Matrix& m2) const
{
	if (rows()!=m2.rows() && cols()!=m2.cols()) {
		throw std::out_of_range("Matrixes not the same size");
	} else {
		Matrix tmpM = Matrix(*this); 
		size_t m_rows = rows(), m_cols = cols();
		for (size_t i = 0; i<m_rows; ++i) {
			for (size_t j = 0; j<m_cols; ++j) {
				tmpM[i][j]+=m2[i][j];
			}
		}
		return tmpM;
	}
	
}

Matrix Matrix::operator* ( const Matrix& b) const {
	int acols = cols(), brows=b.rows();
	if (acols!=brows) {
		throw std::out_of_range("Matrixes not applicable for multiplication");
	} else {
		Matrix retM = Matrix(rows(),b.cols()); 
		const Matrix& a  = *this;
		int le_val;
		for (size_t i = 0; i!=retM.rows(); ++i) {
			for (size_t j = 0; j != retM.cols(); ++j) {
				le_val=0;
				//std::cout<<i<<","<<j<<":"<<std::endl;
				for (size_t k = 0; k!=a.cols(); ++k) {

						//std::cout<<a[i][k]<<" * "<<b[k][j]<<" + "<<std::endl;
						le_val+=a[i][k]*b[k][j];
				}
				//std::cout<<std::endl<<"="<<le_val;
				retM[i][j]=le_val;
			}
			
		}
		return retM;
	}
}

Matrix operator* ( int n, const Matrix& m ) {
	return m*n;
}


